/* 
 * File:   UDPEchoClient-TimeOut.c
 * Author: 云守护
 https://blog.csdn.net/earbao/article/details/16337639
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>



/* 
 * File:   DieWithMessage.h
 * Author: root
 *
 * Created on 2013年11月13日, 下午3:52
 */
 
#ifndef DIEWITHMESSAGE_H
#define	DIEWITHMESSAGE_H
 
#ifdef	__cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
 void  DieWithUserMessage(const char *msg,const char * detail)
 {
     fputs(msg,stderr);
     fputs(":",stderr);
     fputs(detail,stderr);
     fputs("\n",stderr);
     exit(1);
 }
 void DieWithSystemMessage(const char* msg)
 {
     perror(msg);
     exit(1);
 }
 
 
 
#ifdef	__cplusplus
}
#endif
 
#endif	/* DIEWITHMESSAGE_H */
 

//超时设置
static const unsigned int TIMEOUT_SEC=3;
//重连次数
static const unsigned int MAXTRIES=5;
//已经尝试次数
unsigned int tries=0;
//闹铃到来处理函数，超时时间到
void CatchAlarm(int ignored);
int main(int argc, char** argv) {
    if(argc<3||argc>4)
        DieWithUserMessage("param","<Server Address/Name> <Echo Word> [<Server port/service>]\n");
    const char *server=argv[1];
    const char* echoString=argv[2];
    size_t echoStringLen=strlen(echoString);
    if(echoStringLen>1024*8)
        DieWithUserMessage(echoString,"too long");
    const char * service=(argc==4)?argv[3]:"echo";
    //配置地址信息
    struct addrinfo addr_criteria;
    memset(&addr_criteria,0,sizeof(addr_criteria));
    addr_criteria.ai_family=AF_UNSPEC;
    addr_criteria.ai_socktype=SOCK_DGRAM;
    addr_criteria.ai_protocol=IPPROTO_UDP;
    
    struct addrinfo *server_addr;
    //自动获取地址信息
    int ret=getaddrinfo(server,service,&addr_criteria,&server_addr);
    if(ret!=0)
        DieWithUserMessage("getaddrinfo() failed!",gai_strerror(ret));
    //建立socket
    int sock=socket(server_addr->ai_family,server_addr->ai_socktype,server_addr->ai_protocol);
    DieWithSystemMessage("socket failed!");
    if(sock<0)
        DieWithSystemMessage("socket failed!");
    //信号处理者
    struct sigaction handler;
    //设置SIGALRM信号到来处理函数
    handler.sa_handler=CatchAlarm;
    //填充信号掩码
    if(sigfillset(&handler.sa_mask)<0)
        DieWithSystemMessage("sigfillset() failed!");
    //信号标志归零
    handler.sa_flags=0;
    //注册信号量
    if(sigaction(SIGALRM,&handler,0)<0)
        DieWithSystemMessage("sigaction() failed for SIGALRM");
    //开始发送数据
    ssize_t numBytes=sendto(sock,echoString,echoStringLen,0,server_addr->ai_addr,server_addr->ai_addrlen);
    if(numBytes<0)
        DieWithSystemMessage("send() failed!");
    else if(numBytes!=echoStringLen)
    {
        DieWithUserMessage("sendto() error","sent unexpected number of bytes");        
    }
    //然后接收数据，都是循环接收的
    struct sockaddr_storage fromaddr;
    socklen_t fromaddrLen=sizeof(fromaddr);
    //设置定时器，超时设置
    alarm(TIMEOUT_SEC);
    char buffer[BUFSIZ+1];
    //接收数据
    while((numBytes=recvfrom(sock,buffer,BUFSIZ,0,(struct sockaddr*)&fromaddr,&fromaddrLen))<0)
    {
        if(errno=EINTR)//闹铃响了，即超时了。执行了CatchAlarm
        {
            if(tries<MAXTRIES)
            {   //小于重试次数，继续重新发送数据
                numBytes=sendto(sock,echoString,echoStringLen,0,(struct sockaddr*)server_addr->ai_addr,server_addr->ai_addrlen);
                if(numBytes<0)
                    DieWithSystemMessage("sendto() failed!");
                else if(numBytes!=echoStringLen)
                {
                    DieWithUserMessage("sendto() error","sent unexpected number of bytes");
                }
            }else{
                DieWithUserMessage("No Response","Unable to communitcate with server");
            }
        }else{
            DieWithSystemMessage("recvfrom() failed!");
            
        }
    }
    //闹铃归零
    alarm(0);
    buffer[echoStringLen]='\0';
    printf("Received: %s \n",buffer);
    close(sock);
    return (EXIT_SUCCESS);
}
//闹铃响了，执行该函数
void CatchAlarm(int ignored){
    tries+=1;
}
 